package com.javaboy.vhr.config;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

/**
 * @author: gaoyang
 * @date: 2020-12-25 15:29
 * @description: 定义打印日志类
 */
@Slf4j
@Aspect
@Component
public class WebLogAspect {

    private static long beforeTime = 0;

    /**
     * 定义的是切点
     * 方法名就是切点名
     * 使用execution表达式，定义切点为：controller包中的所有方法
     * 第一个*：选中的连接点的方法返回值可以是任意类型，
     * 如* 改为void意思为选中的连接点的方法返回值为void的类型
     * 第二个*：选中的连接点的方法所在类可以是任意名字
     * 第三个*：选中的连接点的方法名字可以是任意名字
     * 第一个..：表示controller下的所有包
     * 第二个..：表示任意参数
     */
    @Pointcut("execution(public * com.javaboy.vhr.controller..*.*(..))")
    public void webLog() {
    }

    /**
     * 前置通知：在连接点之前执行的通知
     *
     * @param joinPoint
     */
    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        //在controller层以外获取当前正在处理的请求，固定格式
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        Signature signature = joinPoint.getSignature();
        String name = signature.getName();

        // 打印请求信息
        log.info("------------- {} 开始 -------------", request.getRequestURI());
        log.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());
        log.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);
        log.info("远程地址: {}", request.getRemoteAddr());

        // 打印请求参数
        Object[] args = joinPoint.getArgs();
        Object[] arguments = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            if (args[i] instanceof ServletRequest
                    || args[i] instanceof ServletResponse
                    || args[i] instanceof MultipartFile) {
                continue;
            }
            arguments[i] = args[i];
        }
        // 排除字段，敏感字段或太长的字段不显示
        String[] excludeProperties = {"shard"};
        PropertyPreFilters filters = new PropertyPreFilters();
        PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();
        excludefilter.addExcludes(excludeProperties);
        log.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter));
        beforeTime = System.currentTimeMillis();
    }

    @After("webLog()")
    public void afterTime() {
        ServletRequestAttributes servletRequestAttributes =
                (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        String uri = request.getRequestURI().toString();
        long betweenTime = System.currentTimeMillis() - beforeTime;
        log.info("------------- {} 结束，耗时：{} ms -------------", request.getRequestURI(), betweenTime);
    }

}
